﻿/*******************************************************************************
 * Copyright (c) 2000, 2004 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Common Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/cpl-v10.html
 * 
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
module dwt.widgets.text;

/****
 <Shawn Liu>

big headache with UTF8 and MBCS.

eg. On Win32 Chinese platform I have two characters "中文". If it is in MBCS format (char[]), the length is 4. If it is in Win32 Unicode format (wchar[]), the length is 2. If it is in D UTF8 format (char[]), the length is 6;

Now I have a Win32 Edit control and to wrap some method with it.
(supose the edit control's text is those 2 characters mentioned above, and the first is selected)

1) getSelection().  return a range contains the start/end position of selection text in Edit.
I use SendMessageA (EM_GETSEL), the result is 0~2;
I use SendMessageW (EM_GETSEL), the result is 0~1;
But neither these two anwers are wrong. To deal with UTF8 string, the result should be 0~3. But how can I get it?
If I use 0~2 or 0~1 to do a slice with the UTF8 string, An access violate exception always raised.

2) setSelection(int start, int end). set text range start~end to be selected.
And the sutiation is same. I call setSelction(0, 3) to select the first character. But where is only 1 or 2 for choose within MBCS/UNICODE format. Though windows doesn't report an error, but more text are selected than expected in most case.

And some other methods have to face the same problem such as getCaretLocation().

*/

private import dwt.dwt;

private import dwt.events.events;
private import dwt.events.listeners;
private import dwt.graphics.font;
private import dwt.graphics.point;
private import dwt.internal.win32.os;
private import dwt.util.eventhandler;
private import dwt.util.util;
private import dwt.widgets.composite;
private import dwt.widgets.display;
private import dwt.widgets.event;
private import dwt.widgets.shell;
private import dwt.widgets.typedlistener;

public import dwt.widgets.scrollable;

private import Utf = tango.text.convert.Utf;


/**
 * Instances of this class are selectable user interface
 * objects that allow the user to enter and modify text.
 * <p>
 * <dl>
 * <dt><b>Styles:</b></dt>
 * <dd>CENTER, LEFT, MULTI, PASSWORD, SINGLE, RIGHT, READ_ONLY, WRAP</dd>
 * <dt><b>Events:</b></dt>
 * <dd>DefaultSelection, Modify, Verify</dd>
 * </dl>
 * <p>
 * Note: Only one of the styles MULTI and SINGLE may be specified. 
 * </p><p>
 * IMPORTANT: This class is <em>not</em> intended to be subclassed.
 * </p>
 */
public class Text : Scrollable {
	int tabs, oldStart, oldEnd;
	boolean doubleClick, ignoreModify, ignoreVerify, ignoreCharacter;
	
	/**
	* The maximum number of characters that can be entered
	* into a text widget.
	*/
	public static int LIMIT;
	
	/**
	* The delimiter used by multi-line text widgets.  When text
	* is queried and from the widget, it will be delimited using
	* this delimiter.
	*/
	public static char[] DELIMITER;
	
	/*
	* This code is intentionally commented.
	*/
//	static final wchar PASSWORD;
	
	/*
	* These values can be different on different platforms.
	* Therefore they are not initialized in the declaration
	* to stop the compiler from inlining.
	*/
	
	static WNDPROC_I EditProc;
	static const char[] EditClass = "EDIT";
	
	private static boolean staticCtor_done = false;
	static void staticCtor() {
		if(staticCtor_done) return;
		staticCtor_done = true;

		LIMIT = OS.IsWinNT ? 0x7FFFFFFF : 0x7FFF;
		DELIMITER = ("\r\n");
		
		WNDCLASS_T lpWndClass;
		OS.GetClassInfo (null, Converter.StrToTCHARz(EditClass), &lpWndClass);
		EditProc = lpWndClass.lpfnWndProc;
		/*
		* This code is intentionally commented.
		*/
//		int hwndText = OS.CreateWindowEx (0,
//			EditClass,
//			null,
//			OS.WS_OVERLAPPED | OS.ES_PASSWORD,
//			0, 0, 0, 0,
//			0,
//			0,
//			OS.GetModuleHandle (null),
//			null);
//		wchar echo = (wchar) OS.SendMessage (hwndText, OS.EM_GETPASSWORDCHAR, 0, 0);
//		OS.DestroyWindow (hwndText);
//		PASSWORD = echo != 0 ? echo : '*';
	}

/**
 * Constructs a new instance of this class given its parent
 * and a style value describing its behavior and appearance.
 * <p>
 * The style value is either one of the style constants defined in
 * class <code>DWT</code> which is applicable to instances of this
 * class, or must be built by <em>bitwise OR</em>'ing together 
 * (that is, using the <code>int</code> "|" operator) two or more
 * of those <code>DWT</code> style constants. The class description
 * lists the style constants that are applicable to the class.
 * Style bits are also inherited from superclasses.
 * </p>
 *
 * @param parent a composite control which will be the parent of the new instance (cannot be null)
 * @param style the style of control to construct
 *
 * @exception IllegalArgumentException <ul>
 *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
 * </ul>
 * @exception SWTException <ul>
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
 *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
 * </ul>
 *
 * @see DWT#SINGLE
 * @see DWT#MULTI
 * @see DWT#READ_ONLY
 * @see DWT#WRAP
 * @see Widget#checkSubclass
 * @see Widget#getStyle
 */
public this (Composite parent, int style) {
	staticCtor();
	
	super (parent, checkStyle (style));
}

LRESULT callWindowProc (int msg, int wParam, int lParam) {
	if (handle is null) return 0;
	return OS.CallWindowProc (EditProc, handle, msg, wParam, lParam);
}

void createHandle () {
	super.createHandle ();
	OS.SendMessage (handle, OS.EM_LIMITTEXT, 0, 0);
}

/**
 * Adds the listener to the collection of listeners who will
 * be notified when the receiver's text is modified, by sending
 * it one of the messages defined in the <code>ModifyListener</code>
 * interface.
 *
 * @param listener the listener which should be notified
 *
 * @exception IllegalArgumentException <ul>
 *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
 * </ul>
 * @exception SWTException <ul>
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
 * </ul>
 *
 * @see ModifyListener
 * @see #removeModifyListener
 */
public void addModifyListener (ModifyListener listener) {
	checkWidget ();
	if (listener is null) error(__FILE__, __LINE__, DWT.ERROR_NULL_ARGUMENT);
	TypedListener typedListener = new TypedListener (listener);
	addListener (DWT.Modify, typedListener);
}

/**
 * Adds the listener to the collection of listeners who will
 * be notified when the control is selected, by sending
 * it one of the messages defined in the <code>SelectionListener</code>
 * interface.
 * <p>
 * <code>widgetSelected</code> is not called for texts.
 * <code>widgetDefaultSelected</code> is typically called when ENTER is pressed in a single-line text.
 * </p>
 *
 * @param listener the listener which should be notified
 *
 * @exception IllegalArgumentException <ul>
 *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
 * </ul>
 * @exception SWTException <ul>
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
 * </ul>
 *
 * @see SelectionListener
 * @see #removeSelectionListener
 * @see SelectionEvent
 */
public void addSelectionListener (SelectionListener listener) {
	checkWidget ();
	if (listener is null) error(__FILE__, __LINE__, DWT.ERROR_NULL_ARGUMENT);
	TypedListener typedListener = new TypedListener (listener);
	addListener (DWT.Selection,typedListener);
	addListener (DWT.DefaultSelection,typedListener);
}

/**
 * Adds the listener to the collection of listeners who will
 * be notified when the receiver's text is verified, by sending
 * it one of the messages defined in the <code>VerifyListener</code>
 * interface.
 *
 * @param listener the listener which should be notified
 *
 * @exception IllegalArgumentException <ul>
 *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
 * </ul>
 * @exception SWTException <ul>
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
 * </ul>
 *
 * @see VerifyListener
 * @see #removeVerifyListener
 */
public void addVerifyListener (VerifyListener listener) {
	checkWidget ();
	if (listener is null) error(__FILE__, __LINE__, DWT.ERROR_NULL_ARGUMENT);
	TypedListener typedListener = new TypedListener (listener);
	addListener (DWT.Verify, typedListener);
}

/**
 * Appends a string.
 * <p>
 * The new text is appended to the text at
 * the end of the widget.
 * </p>
 *
 * @param string the string to be appended
 *
 * @exception IllegalArgumentException <ul>
 *    <li>ERROR_NULL_ARGUMENT - if the string is null</li>
 * </ul>
 * @exception SWTException <ul>
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
 * </ul>
 */
public void append (char[] string) {
	checkWidget ();
	if (string.length == 0) {
		return;
	}
	string = Display.withCrLf (string);
	int length = OS.GetWindowTextLength (handle);

	// <Shawn Liu>
	int length2 = this.getCharCount();
	
	if (hooks (DWT.Verify) || filters (DWT.Verify)) {
		string = verifyText (string, length2, length2, null);
		if (string is null) return;
	}
	OS.SendMessage (handle, OS.EM_SETSEL, length, length);
	/*
	* Feature in Windows.  When an edit control with ES_MULTILINE
	* style that does not have the WS_VSCROLL style is full (i.e.
	* there is no space at the end to draw any more characters),
	* EM_REPLACESEL sends a WM_CHAR with a backspace character
	* to remove any further text that is added.  This is an
	* implementation detail of the edit control that is unexpected
	* and can cause endless recursion when EM_REPLACESEL is sent
	* from a WM_CHAR handler.  The fix is to ignore calling the
	* handler from WM_CHAR.
	*/
	ignoreCharacter = true;
	OS.SendMessage (handle, OS.EM_REPLACESEL, 0, Converter.StrToTCHARz(string));
	ignoreCharacter = false;
	OS.SendMessage (handle, OS.EM_SCROLLCARET, 0, 0);
}

static int checkStyle (int style) {
	style = checkBits (style, DWT.LEFT, DWT.CENTER, DWT.RIGHT, 0, 0, 0);
	if ((style & DWT.SINGLE) != 0) style &= ~(DWT.H_SCROLL | DWT.V_SCROLL | DWT.WRAP);
	if ((style & DWT.WRAP) != 0) style |= DWT.MULTI;
	if ((style & DWT.MULTI) != 0) style &= ~DWT.PASSWORD;
	if ((style & (DWT.SINGLE | DWT.MULTI)) != 0) return style;
	if ((style & (DWT.H_SCROLL | DWT.V_SCROLL)) != 0) return style | DWT.MULTI;
	return style | DWT.SINGLE;
}

/**
 * Clears the selection.
 *
 * @exception SWTException <ul>
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
 * </ul>
 */
public void clearSelection () {
	checkWidget ();
	version(WinCE) {
		/*
		* Bug in WinCE.  Calling EM_SETSEL with -1 and 0 is equivalent
		* to calling EM_SETSEL with 0 and -1.  It causes the entire
		* text to be selected instead of clearing the selection.  The
		* fix is to set the start of the selection to the  end of the
		* current selection.
		*/ 
		int end;
		OS.SendMessage (handle, OS.EM_GETSEL, 0, &end);
		OS.SendMessage (handle, OS.EM_SETSEL, end, end);
	} else {
		OS.SendMessage (handle, OS.EM_SETSEL, -1, 0);
	}
}

public Point computeSize (int wHint, int hHint, boolean changed) {
	checkWidget ();
	int height = 0, width = 0;
	if (wHint == DWT.DEFAULT || hHint == DWT.DEFAULT) {
		HFONT newFont, oldFont;
		HDC hDC = OS.GetDC (handle);
		newFont = cast(HFONT)OS.SendMessage (handle, OS.WM_GETFONT, 0, 0);
		if (newFont !is null) oldFont = OS.SelectObject (hDC, newFont);
		TEXTMETRIC tm;
		OS.GetTextMetrics (hDC, &tm);
		int count = OS.SendMessage (handle, OS.EM_GETLINECOUNT, 0, 0);
		height = count * tm.tmHeight;
		RECT rect;
		int flags = OS.DT_CALCRECT | OS.DT_EDITCONTROL | OS.DT_NOPREFIX;
		boolean wrap = (style & DWT.MULTI) != 0 && (style & DWT.WRAP) != 0;
		if (wrap && wHint != DWT.DEFAULT) {
			flags |= OS.DT_WORDBREAK;
			rect.right = wHint;
		}
		char[] text = getText ();
		TCHAR[] buffer = Converter.StrToTCHARs(text);
		int length = buffer.length;
		if (length != 0) {
			OS.DrawText (hDC, buffer.ptr, length, &rect, flags); 
			width = rect.right - rect.left;
		}
		if (wrap && hHint == DWT.DEFAULT) {
			int newHeight = rect.bottom - rect.top;
			if (newHeight != 0) height = newHeight;
		}
		if (newFont !is null) OS.SelectObject (hDC, oldFont);
		OS.ReleaseDC (handle, hDC);
	}
	if (width == 0) width = DEFAULT_WIDTH;
	if (height == 0) height = DEFAULT_HEIGHT;
	if (wHint != DWT.DEFAULT) width = wHint;
	if (hHint != DWT.DEFAULT) height = hHint;

	/* Calculate the margin width */
	int margins = OS.SendMessage(handle, OS.EM_GETMARGINS, 0, 0);
	int marginWidth = (margins & 0xFFFF) + ((margins >> 16) & 0xFFFF);
	width += marginWidth;

	/*
	* The preferred height of a single-line text widget
	* has been hand-crafted to be the same height as
	* the single-line text widget in an editable combo
	* box.
	*/
	if ((style & DWT.V_SCROLL) != 0) {
		width += OS.GetSystemMetrics (OS.SM_CXVSCROLL);
	}
	if ((style & DWT.H_SCROLL) != 0) {
		height += OS.GetSystemMetrics (OS.SM_CYHSCROLL);
		if ((style & DWT.BORDER) == 0) width++;
	}
	if ((style & DWT.BORDER) != 0) {
		int border = getBorderWidth ();
		width += (border * 2) + 3;
		height += (border * 2) + 3;
	}
	return new Point (width, height);
}

/**
 * Copies the selected text.
 * <p>
 * The current selection is copied to the clipboard.
 * </p>
 *
 * @exception SWTException <ul>
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
 * </ul>
 */
public void copy () {
	checkWidget ();
	OS.SendMessage (handle, OS.WM_COPY, 0, 0);
}

void createWidget () {
	super.createWidget ();
	doubleClick = true;
	setTabStops (tabs = 8);
	fixAlignment ();
}

/**
 * Cuts the selected text.
 * <p>
 * The current selection is first copied to the
 * clipboard and then deleted from the widget.
 * </p>
 *
 * @exception SWTException <ul>
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
 * </ul>
 */
public void cut () {
	checkWidget ();
	OS.SendMessage (handle, OS.WM_CUT, 0, 0);
}

int defaultBackground () {
	return OS.GetSysColor (OS.COLOR_WINDOW);
}

void fixAlignment () {
	/*
	* Feature in Windows.  When the edit control is not
	* mirrored, it uses WS_EX_RIGHT, WS_EX_RTLREADING and
	* WS_EX_LEFTSCROLLBAR to give the control a right to
	* left appearance.  This causes the control to be lead
	* aligned no matter what alignment was specified by
	* the programmer.  For example, setting ES_RIGHT and
	* WS_EX_LAYOUTRTL should cause the contents of the
	* control to be left (trail) aligned in a mirrored world.
	* When the orientation is changed by the user or
	* specified by the programmer, WS_EX_RIGHT conflicts
	* with the mirrored alignment.  The fix is to clear
	* or set WS_EX_RIGHT to achieve the correct alignment
	* according to the orientation and mirroring.
	*/
	if ((style & DWT.MIRRORED) != 0) return;
	int bits0 = OS.GetWindowLong (handle, OS.GWL_EXSTYLE);
	int bits1 = OS.GetWindowLong (handle, OS.GWL_STYLE);
	if ((style & DWT.LEFT_TO_RIGHT) != 0) {
		/*
		* Bug in Windows 98. When the edit control is created
		* with the style ES_RIGHT it automatically sets the 
		* WS_EX_LEFTSCROLLBAR bit.  The fix is to clear the
		* bit when the orientation of the control is left
		* to right.
		*/
		bits0 &= ~OS.WS_EX_LEFTSCROLLBAR;
		if ((style & DWT.RIGHT) != 0) {
			bits0 |= OS.WS_EX_RIGHT;
			bits1 |= OS.ES_RIGHT;
		}
		if ((style & DWT.LEFT) != 0) {
			bits0 &= ~OS.WS_EX_RIGHT;
			bits1 &= ~OS.ES_RIGHT;
		}
	} else {
		if ((style & DWT.RIGHT) != 0) {
			bits0 &= ~OS.WS_EX_RIGHT;
			bits1 &= ~OS.ES_RIGHT;
		}
		if ((style & DWT.LEFT) != 0) {
			bits0 |= OS.WS_EX_RIGHT;
			bits1 |= OS.ES_RIGHT;
		}
	}
	if ((style & DWT.CENTER) != 0) {
		bits1 |= OS.ES_CENTER;
	}	
	OS.SetWindowLong (handle, OS.GWL_EXSTYLE, bits0);
	OS.SetWindowLong (handle, OS.GWL_STYLE, bits1);
}

public int getBorderWidth () {
	checkWidget ();
	/*
	* Feature in Windows 2000 and XP.  Despite the fact that WS_BORDER
	* is set when the edit control is created, the style bit is cleared.
	* The fix is to avoid the check for WS_BORDER and use the DWT widget
	* style bits instead.
	*/
//	if ((style & DWT.BORDER) != 0 && (style & DWT.FLAT) != 0) {
//		return OS.GetSystemMetrics (OS.SM_CXBORDER);
//	}
	return super.getBorderWidth ();
}

/**
 * Gets the line number of the caret.
 * <p>
 * The line number of the caret is returned.
 * </p>
 *
 * @return the line number
 *
 * @exception SWTException <ul>
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
 * </ul>
 */
public int getCaretLineNumber () {
	checkWidget ();
	return OS.SendMessage (handle, OS.EM_LINEFROMCHAR, -1, 0);
}

/**
 * Gets the location the caret.
 * <p>
 * The location of the caret is returned.
 * </p>
 *
 * @return a point, the location of the caret
 *
 * @exception SWTException <ul>
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
 * </ul>
 */
public Point getCaretLocation () {
	checkWidget ();
	/*
	* Bug in Windows.  For some reason, Windows is unable
	* to return the pixel coordinates of the last character
	* in the widget.  The fix is to temporarily insert a
	* space, query the coordinates and delete the space.
	* The selection is always an i-beam in this case because
	* this is the only time the start of the selection can
	* be equal to the last character position in the widget.
	* If EM_POSFROMCHAR fails for any other reason, return
	* pixel coordinates (0,0). 
	*/
	int start;
	OS.SendMessage (handle, OS.EM_GETSEL, &start, 0);
	int pos = OS.SendMessage (handle, OS.EM_POSFROMCHAR, start, 0);
	if (pos == -1) {
		pos = 0;
		if (start >= OS.GetWindowTextLength (handle)) {
			int cp = getCodePage ();
			/*
			* Feature in Windows.  When an edit control with ES_MULTILINE
			* style that does not have the WS_VSCROLL style is full (i.e.
			* there is no space at the end to draw any more characters),
			* EM_REPLACESEL sends a WM_CHAR with a backspace character
			* to remove any further text that is added.  This is an
			* implementation detail of the edit control that is unexpected
			* and can cause endless recursion when EM_REPLACESEL is sent
			* from a WM_CHAR handler.  The fix is to ignore calling the
			* handler from WM_CHAR.
			*/
			TCHAR[] space = " \0";
			TCHAR[] none = "\0";
			ignoreCharacter = ignoreModify = true;
			OS.SendMessage (handle, OS.EM_REPLACESEL, 0, space.ptr);
			pos = OS.SendMessage (handle, OS.EM_POSFROMCHAR, start, 0);
			OS.SendMessage (handle, OS.EM_SETSEL, start, start + 1);
			OS.SendMessage (handle, OS.EM_REPLACESEL, 0, none.ptr);
			ignoreCharacter = ignoreModify = false;
		}
	}
	return new Point (cast(short) (pos & 0xFFFF), cast(short) (pos >> 16));
}

/**
 * Gets the position of the caret.
 * <p>
 * The character position of the caret is returned.
 * </p>
 *
 * @return the position of the caret
 *
 * @exception SWTException <ul>
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
 * </ul>
 */
public int getCaretPosition () {
	checkWidget ();
	int start, end;
	OS.SendMessage (handle, OS.EM_GETSEL, &start, &end);
	int startLine = OS.SendMessage (handle, OS.EM_LINEFROMCHAR, start, 0);
	int caretPos = OS.SendMessage (handle, OS.EM_LINEINDEX, -1, 0);
	int caretLine = OS.SendMessage (handle, OS.EM_LINEFROMCHAR, caretPos, 0);
	int caret = end;
	if (caretLine == startLine) caret = start;
//	if (OS.IsDBLocale) caret = mbcsToWcsPos (caret);
	return caret;
}

/**
 * Gets the number of characters.
 *
 * @return number of characters in the widget
 *
 * @exception SWTException <ul>
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
 * </ul>
 */
public int getCharCount () {
	checkWidget ();
	char[] text = getText();
	
	return text.length;
//	int length = OS.GetWindowTextLength (handle);
//	if (OS.IsDBLocale) length = mbcsToWcsPos (length);
//	return length;
}

char[] getClipboardText () {
	char[] string = "";
	if (OS.OpenClipboard (null)) {
		HANDLE hMem = OS.GetClipboardData (OS.IsUnicode ? OS.CF_UNICODETEXT : OS.CF_TEXT);
		if (hMem !is null) {
			/* Ensure byteCount is a multiple of 2 bytes on UNICODE platforms */
			int Count = OS.GlobalSize (hMem) / TCHAR.sizeof * TCHAR.sizeof;
			void* ptr = OS.GlobalLock (hMem);
			if (ptr !is null) {
				/* Use the character encoding for the default locale */
				string = Converter.TCHARzToStr(cast(TCHAR*)ptr, Count);
				OS.GlobalUnlock (hMem);
			}
		}
		OS.CloseClipboard ();
	}
	return string;
}

/**
 * Gets the double click enabled flag.
 * <p>
 * The double click flag enables or disables the
 * default action of the text widget when the user
 * double clicks.
 * </p>
 * 
 * @return whether or not double click is enabled
 *
 * @exception SWTException <ul>
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
 * </ul>
 */
public boolean getDoubleClickEnabled () {
	checkWidget ();
	return doubleClick;
}

/**
 * Gets the echo character.
 * <p>
 * The echo character is the character that is
 * displayed when the user enters text or the
 * text is changed by the programmer.
 * </p>
 * 
 * @return the echo character
 *
 * @exception SWTException <ul>
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
 * </ul>
 */
public wchar getEchoChar () {
	checkWidget ();
	char echo = cast(char) OS.SendMessageA (handle, OS.EM_GETPASSWORDCHAR, 0, 0);
//	if (echo != 0 && (echo = Display.mbcsToWcs (echo, getCodePage ())) == 0) echo = '*';
	return echo;
}

/**
 * Gets the editable state.
 *
 * @return whether or not the reciever is editable
 * 
 * @exception SWTException <ul>
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
 * </ul>
 */
public boolean getEditable () {
	checkWidget ();
	int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
	return (bits & OS.ES_READONLY) == 0;
}

/**
 * Gets the number of lines.
 *
 * @return the number of lines in the widget
 *
 * @exception SWTException <ul>
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
 * </ul>
 */
public int getLineCount () {
	checkWidget ();
	return OS.SendMessage (handle, OS.EM_GETLINECOUNT, 0, 0);
}

/**
 * Gets the line delimiter.
 *
 * @return a string that is the line delimiter
 *
 * @exception SWTException <ul>
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
 * </ul>
 */
public char[] getLineDelimiter () {
	checkWidget ();
	return DELIMITER;
}

/**
 * Gets the height of a line.
 *
 * @return the height of a row of text
 *
 * @exception SWTException <ul>
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
 * </ul>
 */
public int getLineHeight () {
	checkWidget ();
	HFONT newFont, oldFont;
	HDC hDC = OS.GetDC (handle);
	newFont = cast(HFONT)OS.SendMessage (handle, OS.WM_GETFONT, 0, 0);
	if (newFont !is null) oldFont = OS.SelectObject (hDC, newFont);
	TEXTMETRIC* tm = new TEXTMETRIC();
	OS.GetTextMetrics (hDC, tm);
	if (newFont !is null) OS.SelectObject (hDC, oldFont);
	OS.ReleaseDC (handle, hDC);
	return tm.tmHeight;
}

/**
 * Returns the orientation of the receiver.
 *
 * @return the orientation style
 * 
 * @exception SWTException <ul>
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
 * </ul>
 * 
 * @since 2.1.2
 */
public int getOrientation () {
	checkWidget();
	return style & (DWT.LEFT_TO_RIGHT | DWT.RIGHT_TO_LEFT);
}

/**
 * Gets the position of the selected text.
 * <p>
 * Indexing is zero based.  The range of
 * a selection is from 0..N where N is
 * the number of characters in the widget.
 * </p>
 * 
 * @return the start and end of the selection
 *
 * @exception SWTException <ul>
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
 * </ul>
 */
public Point getSelection () {
	checkWidget ();
	int start, end;
	OS.SendMessage (handle, OS.EM_GETSEL, &start, &end);
	TCHAR[] text = _getText();
	char[] left = Converter.TCHARsToStr(text[0..start]);
	char[] right = Converter.TCHARsToStr(text[0..end]);
	
	return new Point(left.length, right.length);
}

/**
 * Gets the number of selected characters.
 *
 * @return the number of selected characters.
 *
 * @exception SWTException <ul>
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
 * </ul>
 */
public int getSelectionCount () {
	checkWidget ();
	return getSelectionText().length;
}

/**
 * Gets the selected text.
 *
 * @return the selected text
 * 
 * @exception SWTException <ul>
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
 * </ul>
 */
public char[] getSelectionText () {
	checkWidget ();
	int start, end;
	OS.SendMessage (handle, OS.EM_GETSEL, &start, &end);
	TCHAR[] text = _getText();
	return Converter.TCHARsToStr(text[start..end]);
}

/**
 * Gets the number of tabs.
 * <p>
 * Tab stop spacing is specified in terms of the
 * space (' ') character.  The width of a single
 * tab stop is the pixel width of the spaces.
 * </p>
 *
 * @return the number of tab characters
 *
 * @exception SWTException <ul>
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
 * </ul>
 */
public int getTabs () {
	checkWidget ();
	return tabs;
}

int getTabWidth (int tabs) {
	HFONT oldFont = null;
	RECT rect;
	HDC hDC = OS.GetDC (handle);
	HFONT newFont = cast(HFONT)OS.SendMessage (handle, OS.WM_GETFONT, 0, 0);
	if (newFont !is null) oldFont = OS.SelectObject (hDC, newFont);
	int flags = OS.DT_CALCRECT | OS.DT_SINGLELINE | OS.DT_NOPREFIX;
	TCHAR[] SPACE = " ";
	OS.DrawText (hDC, SPACE.ptr, SPACE.length, &rect, flags);
	if (newFont !is null) OS.SelectObject (hDC, oldFont);
	OS.ReleaseDC (handle, hDC);
	return (rect.right - rect.left) * tabs;
}

/**
 * Gets the widget text.
 * <p>
 * The text for a text widget is the characters in the widget.
 * </p>
 *
 * @return the widget text
 *
 * @exception SWTException <ul>
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
 * </ul>
 */
public char[] getText () {
	checkWidget ();
	return OS.GetWindowText (handle);
}

/**
 * <Shawn Liu> added this, internal use only
 */
protected TCHAR[] _getText() {
	
	int len = OS.GetWindowTextLength(handle);
	if(len > 0){
		TCHAR[] buffer = new TCHAR[len + 1];
		len = OS.GetWindowText(handle, buffer.ptr, buffer.length);
		return buffer[0..len];
	}
	return "";
}

/**
 * Gets a range of text.  Returns an empty string if the
 * start of the range is greater than the end.
 * <p>
 * Indexing is zero based.  The range of
 * a selection is from 0..N-1 where N is
 * the number of characters in the widget.
 * </p>
 *
 * @param start the start of the range
 * @param end the end of the range
 * @return the range of text
 *
 * @exception SWTException <ul>
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
 * </ul>
 */
public char[] getText (int start, int end) {
	checkWidget ();
	if (!(start <= end && 0 <= end)) return "";

	char[] text = getText();
		
	start = Math.max (0, start);
	end = Math.min (end, cast(int)text.length);
	return text[start..end];
}

/**
 * Returns the maximum number of characters that the receiver is capable of holding. 
 * <p>
 * If this has not been changed by <code>setTextLimit()</code>,
 * it will be the constant <code>Text.LIMIT</code>.
 * </p>
 * 
 * @return the text limit
 * 
 * @exception SWTException <ul>
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
 * </ul>
 */
public int getTextLimit () {
	checkWidget ();
	return OS.SendMessage (handle, OS.EM_GETLIMITTEXT, 0, 0);
}

/**
 * Returns the zero-relative index of the line which is currently
 * at the top of the receiver.
 * <p>
 * This index can change when lines are scrolled or new lines are added or removed.
 * </p>
 *
 * @return the index of the top line
 *
 * @exception SWTException <ul>
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
 * </ul>
 */
public int getTopIndex () {
	checkWidget ();
	if ((style & DWT.SINGLE) != 0) return 0;
	return OS.SendMessage (handle, OS.EM_GETFIRSTVISIBLELINE, 0, 0);
}

/**
 * Gets the top pixel.
 * <p>
 * The top pixel is the pixel position of the line
 * that is currently at the top of the widget.  On
 * some platforms, a text widget can be scrolled by
 * pixels instead of lines so that a partial line
 * is displayed at the top of the widget.
 * </p><p>
 * The top pixel changes when the widget is scrolled.
 * The top pixel does not include the widget trimming.
 * </p>
 *
 * @return the pixel position of the top line
 *
 * @exception SWTException <ul>
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
 * </ul>
 */
public int getTopPixel () {
	checkWidget ();
	/*
	* Note, EM_GETSCROLLPOS is implemented in Rich Edit 3.0
	* and greater.  The plain text widget and previous versions
	* of Rich Edit return zero.
	*/
	int [] buffer = new int [2];
	int code = OS.SendMessage (handle, OS.EM_GETSCROLLPOS, 0, cast(int*)buffer);
	if (code == 1) return buffer [1];
	return getTopIndex () * getLineHeight ();
}

/**
 * Inserts a string.
 * <p>
 * The old selection is replaced with the new text.
 * </p>
 *
 * @param string the string
 *
 * @exception SWTException <ul>
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
 * </ul>
 */
public void insert (char[] string) {
	checkWidget ();
	if (string.length == 0) 
		return;
		
	string = Display.withCrLf (string);
	if (hooks (DWT.Verify) || filters (DWT.Verify)) {
		Point pt = getSelection();
		string = verifyText (string, pt.x, pt.y, null);
		if (string.length == 0) return;
	}
	/*
	* Feature in Windows.  When an edit control with ES_MULTILINE
	* style that does not have the WS_VSCROLL style is full (i.e.
	* there is no space at the end to draw any more characters),
	* EM_REPLACESEL sends a WM_CHAR with a backspace character
	* to remove any further text that is added.  This is an
	* implementation detail of the edit control that is unexpected
	* and can cause endless recursion when EM_REPLACESEL is sent
	* from a WM_CHAR handler.  The fix is to ignore calling the
	* handler from WM_CHAR.
	*/
	ignoreCharacter = true;
	OS.SendMessage (handle, OS.EM_REPLACESEL, 0, Converter.StrToTCHARz(string));
	ignoreCharacter = false;
}
/*
int mbcsToWcsPos (int mbcsPos) {
	if (mbcsPos == 0) return 0;
	if (OS.IsUnicode) return mbcsPos;
	int cp = getCodePage ();
	int wcsTotal = 0, mbcsTotal = 0;
	char [] buffer = new char [128];
	char[] delimiter = getLineDelimiter();
	int delimiterSize = delimiter.length;
	int count = OS.SendMessageA (handle, OS.EM_GETLINECOUNT, 0, 0);
	for (int line=0; line<count; line++) {
		int wcsSize = 0;
		int linePos = OS.SendMessageA (handle, OS.EM_LINEINDEX, line, 0);
		int mbcsSize = OS.SendMessageA (handle, OS.EM_LINELENGTH, linePos, 0);
		if (mbcsSize != 0) {
			if (mbcsSize + delimiterSize > buffer.length) {
				buffer = new char [mbcsSize + delimiterSize];
			}
			buffer [0] = cast(char) (mbcsSize & 0xFF);
			buffer [1] = cast(char) (mbcsSize >> 8);
			mbcsSize = OS.SendMessageA (handle, OS.EM_GETLINE, line, cast(void*)buffer);
			wcsSize = OS.MultiByteToWideChar (cp, OS.MB_PRECOMPOSED, buffer, mbcsSize, null, 0);
		}
		if (line - 1 != count) {
			for (int i=0; i<delimiterSize; i++) {
				buffer [mbcsSize++] = cast(byte) delimiter[i];
			}
			wcsSize += delimiterSize;
		}
		if ((mbcsTotal + mbcsSize) >= mbcsPos) {
			int bufferSize = mbcsPos - mbcsTotal;
			wcsSize = OS.MultiByteToWideChar (cp, OS.MB_PRECOMPOSED, buffer, bufferSize, null, 0);
			return wcsTotal + wcsSize;
		}
		wcsTotal += wcsSize;
		mbcsTotal += mbcsSize;
	}
	return wcsTotal;
}
*/

/**
 * Pastes text from clipboard.
 * <p>
 * The selected text is deleted from the widget
 * and new text inserted from the clipboard.
 * </p>
 *
 * @exception SWTException <ul>
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
 * </ul>
 */
public void paste () {
	checkWidget ();
	OS.SendMessage (handle, OS.WM_PASTE, 0, 0);
}

/**
 * Removes the listener from the collection of listeners who will
 * be notified when the receiver's text is modified.
 *
 * @param listener the listener which should no longer be notified
 *
 * @exception IllegalArgumentException <ul>
 *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
 * </ul>
 * @exception SWTException <ul>
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
 * </ul>
 *
 * @see ModifyListener
 * @see #addModifyListener
 */
public void removeModifyListener (ModifyListener listener) {
	checkWidget ();
	if (listener is null) error(__FILE__, __LINE__, DWT.ERROR_NULL_ARGUMENT);
	if (eventTable is null) return;
	eventTable.unhook (DWT.Modify, listener);	
}

/**
 * Removes the listener from the collection of listeners who will
 * be notified when the control is selected.
 *
 * @param listener the listener which should be notified
 *
 * @exception IllegalArgumentException <ul>
 *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
 * </ul>
 * @exception SWTException <ul>
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
 * </ul>
 *
 * @see SelectionListener
 * @see #addSelectionListener
 */
public void removeSelectionListener (SelectionListener listener) {
	checkWidget ();
	if (listener is null) error(__FILE__, __LINE__, DWT.ERROR_NULL_ARGUMENT);
	if (eventTable is null) return;
	eventTable.unhook (DWT.Selection, listener);
	eventTable.unhook (DWT.DefaultSelection,listener);	
}

/**
 * Removes the listener from the collection of listeners who will
 * be notified when the control is verified.
 *
 * @param listener the listener which should be notified
 *
 * @exception IllegalArgumentException <ul>
 *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
 * </ul>
 * @exception SWTException <ul>
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
 * </ul>
 *
 * @see VerifyListener
 * @see #addVerifyListener
 */
public void removeVerifyListener (VerifyListener listener) {
	checkWidget ();
	if (listener is null) error(__FILE__, __LINE__, DWT.ERROR_NULL_ARGUMENT);
	if (eventTable is null) return;
	eventTable.unhook (DWT.Verify, listener);	
}

/**
 * Selects all the text in the receiver.
 *
 * @exception SWTException <ul>
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
 * </ul>
 */
public void selectAll () {
	checkWidget ();
	OS.SendMessage (handle, OS.EM_SETSEL, 0, -1);
}

boolean sendKeyEvent (int type, int msg, int wParam, int lParam, Event event) {
	if (!super.sendKeyEvent (type, msg, wParam, lParam, event)) {
		return false;
	}
	if (ignoreVerify) return true;
	if (type != DWT.KeyDown) return true;
	if (msg != OS.WM_CHAR && msg != OS.WM_KEYDOWN && msg != OS.WM_IME_CHAR) {
		return true;
	}
	if (event.character == 0) return true;
	if (!hooks (DWT.Verify) && !filters (DWT.Verify)) return true;
	wchar key = event.character;
	int stateMask = event.stateMask;
	
	/*
	* Disable all magic keys that could modify the text
	* and don't send events when Alt, Shift or Ctrl is
	* pressed.
	*/
	switch (msg) {
		case OS.WM_CHAR:
			if (key != 0x08 && key != 0x7F && key != '\r' && key != '\t' && key != '\n') break;
			// FALL THROUGH
		case OS.WM_KEYDOWN:
			if ((stateMask & (DWT.ALT | DWT.SHIFT | DWT.CONTROL)) != 0) return false;
			break;
		default : break;
	}

	/*
	* If the left button is down, the text widget refuses the character.
	*/
	if (OS.GetKeyState (OS.VK_LBUTTON) < 0) {
		return true;
	}

	/* Verify the character */
	char[] oldText = "";
	int start, end;
	OS.SendMessage (handle, OS.EM_GETSEL, &start, &end);
	switch (key) {
		case 0x08:	/* Bs */
			if (start == end) {
				if (start == 0) return true;
				int lineStart = OS.SendMessage (handle, OS.EM_LINEINDEX, -1, 0);
				if (start == lineStart) {
					start = start  - DELIMITER.length;
				} else {
					start = start - 1;
/*					if (OS.IsDBLocale) {
						int newStart, newEnd;
						OS.SendMessage (handle, OS.EM_SETSEL, start, end);
						OS.SendMessage (handle, OS.EM_GETSEL, &newStart, cast(int)&newEnd);
						if (start != newStart) start = start - 1;
					}*/
				}
				start = Math.max (start, 0);
			}
			break;
		case 0x7F:	/* Del */
			if (start == end) {
				int length = OS.GetWindowTextLength (handle);
				if (start == length) return true;
				int line = OS.SendMessage (handle, OS.EM_LINEFROMCHAR, end, 0);
				int lineStart = OS.SendMessage (handle, OS.EM_LINEINDEX, line + 1, 0);
				if (end == lineStart - DELIMITER.length) {
					end = end + DELIMITER.length;
				} else {
					end = end + 1;
/*					if (OS.IsDBLocale) {
						int [] newStart = new int [1], newEnd = new int [1];
						OS.SendMessage (handle, OS.EM_SETSEL, start, end);
						OS.SendMessage (handle, OS.EM_GETSEL, &newStart, cast(int)&newEnd);
						if (end != newEnd [0]) end = end + 1;
					}*/
				}
				end = Math.min (end, length);
			}
			break;
		case '\r':	/* Return */
			if ((style & DWT.SINGLE) != 0) return true;
			oldText = DELIMITER;
			break;
		default:	/* Tab and other characters */
			if (key != '\t' && key < 0x20) return true;
			wchar[] wcharArr;
			wcharArr ~= key;
			oldText = Utf.toString(wcharArr);
			Util.trace(oldText);
			break;
	}
	char[] newText = verifyText (oldText, start, end, event);
	if (newText is null) return false;
	if (newText == oldText) return true;
	newText = Display.withCrLf (newText);

	Util.trace(newText);
	OS.SendMessage (handle, OS.EM_SETSEL, start, end);
	/*
	* Feature in Windows.  When an edit control with ES_MULTILINE
	* style that does not have the WS_VSCROLL style is full (i.e.
	* there is no space at the end to draw any more characters),
	* EM_REPLACESEL sends a WM_CHAR with a backspace character
	* to remove any further text that is added.  This is an
	* implementation detail of the edit control that is unexpected
	* and can cause endless recursion when EM_REPLACESEL is sent
	* from a WM_CHAR handler.  The fix is to ignore calling the
	* handler from WM_CHAR.
	*/
	ignoreCharacter = true;
	OS.SendMessage (handle, OS.EM_REPLACESEL, 0, Converter.StrToTCHARz(newText));
	ignoreCharacter = false;
	return false;
}

void setBounds5 (int x, int y, int width, int height, int flags) {
	/*
	* Feature in Windows.  When the caret is moved,
	* the text widget scrolls to show the new location.
	* This means that the text widget may be scrolled
	* to the right in order to show the caret when the
	* widget is not large enough to show both the caret
	* location and all the text.  Unfortunately, when
	* the text widget is resized such that all the text
	* and the caret could be visible, Windows does not
	* scroll the widget back.  The fix is to resize the
	* text widget, set the selection to the start of the
	* text and then restore the selection.  This will
	* cause the text widget compute the correct scroll
	* position.
	*/
	if ((flags & OS.SWP_NOSIZE) == 0 && width != 0) {
		RECT rect;
		OS.GetWindowRect (handle, &rect);
		if (rect.right - rect.left == 0) {
			int start, end;
			OS.SendMessage (handle, OS.EM_GETSEL, &start, &end);
			if (start != 0 || end != 0) {
				SetWindowPos (handle, null, x, y, width, height, flags);
				OS.SendMessage (handle, OS.EM_SETSEL, 0, 0);
				OS.SendMessage (handle, OS.EM_SETSEL, start, end);
				return;
			}
		}
	}
	super.setBounds5 (x, y, width, height, flags);
}

/**
 * Sets the double click enabled flag.
 * <p>
 * The double click flag enables or disables the
 * default action of the text widget when the user
 * double clicks.
 * </p>
 * 
 * @param doubleClick the new double click flag
 *
 * @exception SWTException <ul>
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
 * </ul>
 */
public void setDoubleClickEnabled (boolean doubleClick) {
	checkWidget ();
	this.doubleClick = doubleClick;
}

/**
 * Sets the echo character.
 * <p>
 * The echo character is the character that is
 * displayed when the user enters text or the
 * text is changed by the programmer. Setting
 * the echo character to '\0' clears the echo
 * character and redraws the original text.
 * If for any reason the echo character is invalid,
 * the default echo character for the platform
 * is used.
 * </p>
 *
 * @param echo the new echo character
 *
 * @exception SWTException <ul>
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
 * </ul>
 */
public void setEchoChar (char echo) {
	checkWidget ();
	if ((style & DWT.MULTI) != 0) return;
//	if (echo != 0) {
//		if ((echo = cast(wchar) Display.wcsToMbcs (echo, getCodePage ())) == 0) echo = '*';
//	}
	OS.SendMessage (handle, OS.EM_SETPASSWORDCHAR, echo, 0);
	/*
	* Bug in Windows.  When the password character is changed,
	* Windows does not redraw to show the new password character.
	* The fix is to force a redraw when the character is set.
	*/
	OS.InvalidateRect (handle, null, true);
}

/**
 * Sets the editable state.
 *
 * @param editable the new editable state
 *
 * @exception SWTException <ul>
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
 * </ul>
 */
public void setEditable (boolean editable) {
	checkWidget ();
	style &= ~DWT.READ_ONLY;
	if (!editable) style |= DWT.READ_ONLY; 
	OS.SendMessage (handle, OS.EM_SETREADONLY, editable ? 0 : 1, 0);
}

public void setFont (Font font) {
	checkWidget ();
	super.setFont (font);
	setTabStops (tabs);
}

/**
 * Sets the orientation of the receiver, which must be one
 * of the constants <code>DWT.LEFT_TO_RIGHT</code> or <code>DWT.RIGHT_TO_LEFT</code>.
 * <p>
 *
 * @param orientation new orientation style
 * 
 * @exception SWTException <ul>
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
 * </ul>
 * 
 * @since 2.1.2
 */
public void setOrientation (int orientation) {
	checkWidget();
	version(WinCE) { return; }
	if ((OS.WIN32_MAJOR << 16 | OS.WIN32_MINOR) < (4 << 16 | 10)) return;
	int flags = DWT.RIGHT_TO_LEFT | DWT.LEFT_TO_RIGHT;
	if ((orientation & flags) == 0 || (orientation & flags) == flags) return;
	style &= ~flags;
	style |= orientation & flags;
	int bits  = OS.GetWindowLong (handle, OS.GWL_EXSTYLE);
	if ((style & DWT.RIGHT_TO_LEFT) != 0) {
		bits |= OS.WS_EX_RTLREADING | OS.WS_EX_LEFTSCROLLBAR;
	} else {
		bits &= ~(OS.WS_EX_RTLREADING | OS.WS_EX_LEFTSCROLLBAR);
	}
	OS.SetWindowLong (handle, OS.GWL_EXSTYLE, bits);
	fixAlignment ();
}

/**
 * Sets the selection.
 * <p>
 * Indexing is zero based.  The range of
 * a selection is from 0..N where N is
 * the number of characters in the widget.
 * </p><p>
 * Text selections are specified in terms of
 * caret positions.  In a text widget that
 * contains N characters, there are N+1 caret
 * positions, ranging from 0..N.  This differs
 * from other functions that address character
 * position such as getText () that use the
 * regular array indexing rules.
 * </p>
 *
 * @param start new caret position
 *
 * @exception SWTException <ul>
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
 * </ul>
 */
public void setSelection (int start) {
	checkWidget ();
//	if (OS.IsDBLocale) start = wcsToMbcsPos (start);
	char[] text= getText();

	if(start > text.length)
		return;
	char[] sub = text[0..start];
	TCHAR[] tc = Converter.StrToTCHARs(sub);
	start = tc.length;
	
	OS.SendMessage (handle, OS.EM_SETSEL, start, start);
	OS.SendMessage (handle, OS.EM_SCROLLCARET, 0, 0);
}

/**
 * Sets the selection.
 * <p>
 * Indexing is zero based.  The range of
 * a selection is from 0..N where N is
 * the number of characters in the widget.
 * </p><p>
 * Text selections are specified in terms of
 * caret positions.  In a text widget that
 * contains N characters, there are N+1 caret
 * positions, ranging from 0..N.  This differs
 * from other functions that address character
 * position such as getText () that use the
 * usual array indexing rules.
 * </p>
 *
 * @param start the start of the range
 * @param end the end of the range
 *
 * @exception SWTException <ul>
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
 * </ul>
 */
public void setSelection (int start, int end) {
	checkWidget ();
	char[] text= getText();

	if(start > text.length || end > text.length || start > end)
		return;
	char[] left = text[0..start];
	char[] right = text[0..end];
	TCHAR[] tc1 = Converter.StrToTCHARs(left);
	TCHAR[] tc2 = Converter.StrToTCHARs(right);	
	start = tc1.length;
	end = tc2.length;
	
	OS.SendMessage (handle, OS.EM_SETSEL, start, end);
	OS.SendMessage (handle, OS.EM_SCROLLCARET, 0, 0);
}

public void setRedraw (boolean redraw) {
	checkWidget ();
	super.setRedraw (redraw);
	/*
	* Feature in Windows.  When WM_SETREDRAW is used to turn
	* redraw off, the edit control is not scrolled to show the
	* i-beam.  The fix is to detect that the i-beam has moved
	* while redraw is turned off and force it to be visible
	* when redraw is restored.
	*/
	if (drawCount != 0) return;
	int start, end;
	OS.SendMessage (handle, OS.EM_GETSEL, &start, &end);
	if (!redraw) {
		oldStart = start;  oldEnd = end;
	} else {
		if (oldStart == start && oldEnd == end) return;
		OS.SendMessage (handle, OS.EM_SCROLLCARET, 0, 0);
	}
}

/**
 * Sets the selection.
 * <p>
 * Indexing is zero based.  The range of
 * a selection is from 0..N where N is
 * the number of characters in the widget.
 * </p><p>
 * Text selections are specified in terms of
 * caret positions.  In a text widget that
 * contains N characters, there are N+1 caret
 * positions, ranging from 0..N.  This differs
 * from other functions that address character
 * position such as getText () that use the
 * usual array indexing rules.
 * </p>
 *
 * @param selection the point
 *
 * @exception IllegalArgumentException <ul>
 *    <li>ERROR_NULL_ARGUMENT - if the point is null</li>
 * </ul>
 * @exception SWTException <ul>
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
 * </ul>
 */
public void setSelection (Point selection) {
	checkWidget ();
	if (selection is null) error(__FILE__, __LINE__, DWT.ERROR_NULL_ARGUMENT);
	setSelection (selection.x, selection.y);
}

/**
 * Sets the number of tabs.
 * <p>
 * Tab stop spacing is specified in terms of the
 * space (' ') character.  The width of a single
 * tab stop is the pixel width of the spaces.
 * </p>
 *
 * @param tabs the number of tabs
 *
 * </ul>
 * @exception SWTException <ul>
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
 * </ul>
 */
public void setTabs (int tabs) {
	checkWidget ();
	if (tabs < 0) return;
	setTabStops (this.tabs = tabs);
}

void setTabStops (int tabs) {
	/*
	* Feature in Windows.  Windows expects the tab spacing in
	* dialog units so we must convert from space widths.  Due
	* to round off error, the tab spacing may not be the exact
	* number of space widths, depending on the font.
	*/
	int width = (getTabWidth (tabs) * 4) / (OS.GetDialogBaseUnits () & 0xFFFF);
	OS.SendMessage (handle, OS.EM_SETTABSTOPS, 1, &width);
}

/**
 * Sets the contents of the receiver to the given string. If the receiver has style
 * SINGLE and the argument contains multiple lines of text, the result of this
 * operation is undefined and may vary from platform to platform.
 *
 * @param string the new text
 *
 * @exception IllegalArgumentException <ul>
 *    <li>ERROR_NULL_ARGUMENT - if the string is null</li>
 * </ul>
 * @exception SWTException <ul>
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
 * </ul>
 */
public void setText (char[] string) {
	checkWidget ();
	if (string is null) {
		string = "";
	}
	string = Display.withCrLf (string);
	if (hooks (DWT.Verify) || filters (DWT.Verify)) {
		char[] text = getText();
		string = verifyText (string, 0, text.length, null);
		if (string is null) return;
	}
	OS.SetWindowText (handle, Converter.StrToTCHARz(string));
	/*
	* Bug in Windows.  When the widget is multi line
	* text widget, it does not send a WM_COMMAND with
	* control code EN_CHANGE from SetWindowText () to
	* notify the application that the text has changed.
	* The fix is to send the event.
	*/
	if ((style & DWT.MULTI) != 0) {
		sendEvent (DWT.Modify);
		// widget could be disposed at this point
	}
}

/**
 * Sets the maximum number of characters that the receiver
 * is capable of holding to be the argument.
 * <p>
 * Instead of trying to set the text limit to zero, consider
 * creating a read-only text widget.
 * </p><p>
 * To reset this value to the default, use <code>setTextLimit(Text.LIMIT)</code>.
 * </p>
 *
 * @param limit new text limit
 *
 * @exception IllegalArgumentException <ul>
 *    <li>ERROR_CANNOT_BE_ZERO - if the limit is zero</li>
 * </ul>
 * @exception SWTException <ul>
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
 * </ul>
 */
public void setTextLimit (int limit) {
	checkWidget ();
	if (limit == 0) error(__FILE__, __LINE__, DWT.ERROR_CANNOT_BE_ZERO);
	OS.SendMessage (handle, OS.EM_SETLIMITTEXT, limit, 0);
}

/**
 * Sets the zero-relative index of the line which is currently
 * at the top of the receiver. This index can change when lines
 * are scrolled or new lines are added and removed.
 *
 * @param index the index of the top item
 *
 * @exception SWTException <ul>
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
 * </ul>
 */
public void setTopIndex (int index) {
	checkWidget ();
	if ((style & DWT.SINGLE) != 0) return;
	int count = OS.SendMessage (handle, OS.EM_GETLINECOUNT, 0, 0);
	index = Math.min (Math.max (index, 0), count - 1);
	int topIndex = OS.SendMessage (handle, OS.EM_GETFIRSTVISIBLELINE, 0, 0);
	OS.SendMessage (handle, OS.EM_LINESCROLL, 0, index - topIndex);
}

/**
 * Shows the selection.
 * <p>
 * If the selection is already showing
 * in the receiver, this method simply returns.  Otherwise,
 * lines are scrolled until the selection is visible.
 * </p>
 * 
 * @exception SWTException <ul>
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
 * </ul>
 */
public void showSelection () {
	checkWidget ();
	OS.SendMessage (handle, OS.EM_SCROLLCARET, 0, 0);
}

char[] verifyText (char[] string, int start, int end, Event keyEvent) {
	if (ignoreVerify) return string;
	Event event = new Event ();
	event.text = string;
	event.start = start;
	event.end = end;
	if (keyEvent !is null) {
		event.character = keyEvent.character;
		event.keyCode = keyEvent.keyCode;
		event.stateMask = keyEvent.stateMask;
	}
//	if (OS.IsDBLocale) {
//		event.start = mbcsToWcsPos (start);
//		event.end = mbcsToWcsPos (end);
//	}
	/*
	* It is possible (but unlikely), that application
	* code could have disposed the widget in the verify
	* event.  If this happens, answer null to cancel
	* the operation.
	*/
	sendEvent (DWT.Verify, event);
	if (!event.doit || isDisposed ()) return null;
	return event.text;
}

/*
int wcsToMbcsPos (int wcsPos) {
	if (wcsPos == 0) return 0;
	if (OS.IsUnicode) return wcsPos;
	int cp = getCodePage ();
	int wcsTotal = 0, mbcsTotal = 0;
	char [] buffer = new char [128];
	char[] delimiter = getLineDelimiter ();
	int delimiterSize = delimiter.length;
	int count = OS.SendMessageA (handle, OS.EM_GETLINECOUNT, 0, 0);
	for (int line=0; line<count; line++) {
		int wcsSize = 0;
		int linePos = OS.SendMessageA (handle, OS.EM_LINEINDEX, line, 0);
		int mbcsSize = OS.SendMessageA (handle, OS.EM_LINELENGTH, linePos, 0);
		if (mbcsSize != 0) {
			if (mbcsSize + delimiterSize > buffer.length) {
				buffer = new char [mbcsSize + delimiterSize];
			}
			//ENDIAN
			buffer [0] = cast(char) (mbcsSize & 0xFF);
			buffer [1] = cast(char) (mbcsSize >> 8);
			mbcsSize = OS.SendMessageA (handle, OS.EM_GETLINE, line, cast(void*)buffer);
			wcsSize = OS.MultiByteToWideChar (cp, OS.MB_PRECOMPOSED, buffer, mbcsSize, null, 0);
		}
		if (line - 1 != count) {
			for (int i=0; i<delimiterSize; i++) {
				buffer [mbcsSize++] = cast(byte) delimiter[i];
			}
			wcsSize += delimiterSize;
		}
		if ((wcsTotal + wcsSize) >= wcsPos) {
			wcsSize = 0;
			int index = 0;
			while (index < mbcsSize) {
				if ((wcsTotal + wcsSize) == wcsPos) {
					return mbcsTotal + index;
				}
				if (OS.IsDBCSLeadByte (buffer [index++])) index++;
				wcsSize++;
			}
			return mbcsTotal + mbcsSize;
		}
		wcsTotal += wcsSize;
		mbcsTotal += mbcsSize;
	}
	return mbcsTotal;
}
*/
int widgetStyle () {
	int bits = super.widgetStyle () | OS.ES_AUTOHSCROLL;
	if ((style & DWT.PASSWORD) != 0) bits |= OS.ES_PASSWORD;
	if ((style & DWT.CENTER) != 0) bits |= OS.ES_CENTER;
	if ((style & DWT.RIGHT) != 0) bits |= OS.ES_RIGHT;
	if ((style & DWT.READ_ONLY) != 0) bits |= OS.ES_READONLY;
	if ((style & DWT.SINGLE) != 0) return bits;
	bits |= OS.ES_MULTILINE | OS.ES_NOHIDESEL | OS.ES_AUTOVSCROLL;	
	if ((style & DWT.WRAP) != 0) bits &= ~(OS.WS_HSCROLL | OS.ES_AUTOHSCROLL);
	return bits;
}

char[] windowClass() {
	return EditClass;
}

WNDPROC_I windowProc2 () {
	return EditProc;
}

LDWTRESULT WM_CHAR_ (int wParam, int lParam) {
	if (ignoreCharacter) return null;
	LDWTRESULT result = super.WM_CHAR_ (wParam, lParam);
	if (result !is null) return result;
	/*
	* Feature in Windows.  For some reason, when the
	* widget is a single line text widget, when the
	* user presses tab, return or escape, Windows beeps.
	* The fix is to look for these keys and not call
	* the window proc.
	*/
	if ((style & DWT.SINGLE) != 0) {
		switch (wParam) {
			case OS.VK_RETURN:
				postEvent (DWT.DefaultSelection);
				// FALL THROUGH
			case OS.VK_TAB:
			case OS.VK_ESCAPE: return LDWTRESULT.ZERO;
			default : break;
		}
	}
	return result;
}

LDWTRESULT WM_CLEAR_ (int wParam, int lParam) {
	LDWTRESULT result = super.WM_CLEAR_ (wParam, lParam);
	if (result !is null) return result;
	
	if (!hooks (DWT.Verify) && !filters (DWT.Verify)) return result;
	if ((style & DWT.READ_ONLY) != 0) return result;
	
	int start, end;
	OS.SendMessage (handle, OS.EM_GETSEL, &start, &end);
	if (start == end) return result;

	Point pt = getSelection();
	char[] newText = verifyText ("", pt.x, pt.y, null);
	if (newText is null) return LDWTRESULT.ZERO;
	if (newText.length != 0) {
		result = new LDWTRESULT (callWindowProc (OS.WM_CLEAR, 0, 0));	
		newText = Display.withCrLf (newText);
		/*
		* Feature in Windows.  When an edit control with ES_MULTILINE
		* style that does not have the WS_VSCROLL style is full (i.e.
		* there is no space at the end to draw any more characters),
		* EM_REPLACESEL sends a WM_CHAR with a backspace character
		* to remove any further text that is added.  This is an
		* implementation detail of the edit control that is unexpected
		* and can cause endless recursion when EM_REPLACESEL is sent
		* from a WM_CHAR handler.  The fix is to ignore calling the
		* handler from WM_CHAR.
		*/
		ignoreCharacter = true;
		OS.SendMessage (handle, OS.EM_REPLACESEL, 0, Converter.StrToTCHARz(newText));
		ignoreCharacter = false;
	}
	return result;
}

LDWTRESULT WM_CUT_ (int wParam, int lParam) {
	LDWTRESULT result = super.WM_CUT_ (wParam, lParam);
	if (result !is null) return result;
	if (!hooks (DWT.Verify) && !filters (DWT.Verify)) return result;
	if ((style & DWT.READ_ONLY) != 0) return result;
	int start, end;
	OS.SendMessage (handle, OS.EM_GETSEL, &start, &end);
	if (start == end) return result;

	Point pt = getSelection();
	char[] newText = verifyText ("", pt.x, pt.y, null);
	
	if (newText is null) return LDWTRESULT.ZERO;
	if (newText.length != 0) {
		result = new LDWTRESULT (callWindowProc (OS.WM_CUT, 0, 0));	
		newText = Display.withCrLf (newText);
		/*
		* Feature in Windows.  When an edit control with ES_MULTILINE
		* style that does not have the WS_VSCROLL style is full (i.e.
		* there is no space at the end to draw any more characters),
		* EM_REPLACESEL sends a WM_CHAR with a backspace character
		* to remove any further text that is added.  This is an
		* implementation detail of the edit control that is unexpected
		* and can cause endless recursion when EM_REPLACESEL is sent
		* from a WM_CHAR handler.  The fix is to ignore calling the
		* handler from WM_CHAR.
		*/
		ignoreCharacter = true;
		OS.SendMessage (handle, OS.EM_REPLACESEL, 0, Converter.StrToTCHARz(newText));
		ignoreCharacter = false;
	}
	return result;
}

LDWTRESULT WM_GETDLGCODE_ (int wParam, int lParam) {
	LDWTRESULT result = super.WM_GETDLGCODE_ (wParam, lParam);
	if (result !is null) return result;
	
	/*
	* Bug in WinCE PPC.  For some reason, sending WM_GETDLGCODE
	* to a multi-line edit control causes it to ignore return and
	* tab keys.  The fix is to return the value which is normally
	* returned by the text window proc on other versions of Windows.
	*/
	if (OS.IsPPC) {
		if ((style & DWT.MULTI) != 0 && (style & DWT.READ_ONLY) == 0 && lParam == 0) {
			return new LDWTRESULT (OS.DLGC_HASSETSEL | OS.DLGC_WANTALLKEYS | OS.DLGC_WANTCHARS);
		}
	}

	/*
	* Feature in Windows.  Despite the fact that the
	* edit control is read only, it still returns a
	* dialog code indicating that it wants keys.  The
	* fix is to detect this case and clear the bits.
	*/
	if ((style & DWT.READ_ONLY) != 0) {
		int code = callWindowProc (OS.WM_GETDLGCODE, wParam, lParam);
		code &= ~(OS.DLGC_WANTALLKEYS | OS.DLGC_WANTTAB | OS.DLGC_WANTARROWS);
		return new LDWTRESULT (code);
	}
	return null;
}

LDWTRESULT WM_IME_CHAR_ (int wParam, int lParam) {

	/* Process a DBCS character */
	Display display = this.display;
	display.lastKey = 0;
	display.lastAscii = wParam;
	display.lastVirtual = display.lastNull = display.lastDead = false;
	if (!super.sendKeyEvent (DWT.KeyDown, OS.WM_IME_CHAR, wParam, lParam)) {
		return LDWTRESULT.ZERO;
	}

	/*
	* Feature in Windows.  The Windows text widget uses
	* two 2 WM_CHAR's to process a DBCS key instead of
	* using WM_IME_CHAR.  The fix is to allow the text
	* widget to get the WM_CHAR's but ignore sending
	* them to the application.
	*/
	ignoreCharacter = true;
	int result = callWindowProc (OS.WM_IME_CHAR, wParam, lParam);
	MSG msg;
	int flags = OS.PM_REMOVE | OS.PM_NOYIELD | OS.PM_QS_INPUT | OS.PM_QS_POSTMESSAGE;
	while (OS.PeekMessage (&msg, handle, OS.WM_CHAR, OS.WM_CHAR, flags)) {
		OS.TranslateMessage (&msg);
		OS.DispatchMessage (&msg);
	}
	ignoreCharacter = false;
	
	super.sendKeyEvent (DWT.KeyUp, OS.WM_IME_CHAR, wParam, lParam);
	// widget could be disposed at this point
	display.lastKey = display.lastAscii = 0;
	return new LDWTRESULT (result);
}

LDWTRESULT WM_LBUTTONDBLCLK_ (int wParam, int lParam) {
	/*
	* Prevent Windows from processing WM_LBUTTONDBLCLK
	* when double clicking behavior is disabled by not
	* calling the window proc.
	*/
	sendMouseEvent (DWT.MouseDown, 1, OS.WM_LBUTTONDOWN, wParam, lParam);
	sendMouseEvent (DWT.MouseDoubleClick, 1, OS.WM_LBUTTONDBLCLK, wParam, lParam);
	if (OS.GetCapture () !is handle) OS.SetCapture (handle);
	if (!doubleClick) return LDWTRESULT.ZERO;
		
	/*
	* Bug in Windows.  When the last line of text in the
	* widget is double clicked and the line is empty, Windows
	* hides the i-beam then moves it to the first line in
	* the widget but does not scroll to show the user.
	* If the user types without clicking the mouse, invalid
	* characters are displayed at the end of each line of
	* text in the widget.  The fix is to detect this case
	* and avoid calling the window proc.
	*/
	int start, end;
	OS.SendMessage (handle, OS.EM_GETSEL, &start, &end);
	if (start == end) {
		int length = OS.GetWindowTextLength (handle);
		if (length == start) {
			int result = OS.SendMessage (handle, OS.EM_LINELENGTH, length, 0);
			if (result == 0) return LDWTRESULT.ZERO;
		}
	}
	return null;
}

LDWTRESULT WM_PASTE_ (int wParam, int lParam) {
	LDWTRESULT result = super.WM_PASTE_ (wParam, lParam);
	if (result !is null) return result;
	if (!hooks (DWT.Verify) && !filters (DWT.Verify)) return result;
	if ((style & DWT.READ_ONLY) != 0) return result;
	char[] oldText = getClipboardText ();
	if (oldText is null) return result;
//	int start, end;
//	OS.SendMessage (handle, OS.EM_GETSEL, &start, &end);
//	char[] newText = verifyText (oldText, start, end, null);
	
	Point pt = getSelection();
	char[] newText = verifyText (oldText, pt.x, pt.y, null);
	if (newText is null) return LDWTRESULT.ZERO;
	if (newText != oldText) {
		newText = Display.withCrLf (newText);
		/*
		* Feature in Windows.  When an edit control with ES_MULTILINE
		* style that does not have the WS_VSCROLL style is full (i.e.
		* there is no space at the end to draw any more characters),
		* EM_REPLACESEL sends a WM_CHAR with a backspace character
		* to remove any further text that is added.  This is an
		* implementation detail of the edit control that is unexpected
		* and can cause endless recursion when EM_REPLACESEL is sent
		* from a WM_CHAR handler.  The fix is to ignore calling the
		* handler from WM_CHAR.
		*/
		ignoreCharacter = true;
		OS.SendMessage (handle, OS.EM_REPLACESEL, 0, Converter.StrToTCHARz(newText));
		ignoreCharacter = false;
		return LDWTRESULT.ZERO;
	}
	return result;
}

LDWTRESULT WM_UNDO_ (int wParam, int lParam) {
	LDWTRESULT result = super.WM_UNDO_ (wParam, lParam);
	if (result !is null) return result;
	if (!hooks (DWT.Verify) && !filters (DWT.Verify)) {
		return result;
	}

	/* Undo and then Redo to get the Undo text */
	if (OS.SendMessage (handle, OS.EM_CANUNDO, 0, 0) == 0) {
		return result;
	}
	ignoreVerify = true;
	callWindowProc (OS.WM_UNDO, wParam, lParam);
	char[] oldText = getSelectionText ();
	callWindowProc (OS.WM_UNDO, wParam, lParam);
	ignoreVerify = false;

	/* Verify the Undo operation */
//	int start, end;
//	OS.SendMessage (handle, OS.EM_GETSEL, &start, &end);	
//	char[] newText = verifyText (oldText, start, end, null);

	Point pt = getSelection();
	char[] newText = verifyText (oldText, pt.x, pt.y, null);
	
	if (newText is null) return LDWTRESULT.ZERO;
	if (newText != oldText) {
		newText = Display.withCrLf (newText);
		/*
		* Feature in Windows.  When an edit control with ES_MULTILINE
		* style that does not have the WS_VSCROLL style is full (i.e.
		* there is no space at the end to draw any more characters),
		* EM_REPLACESEL sends a WM_CHAR with a backspace character
		* to remove any further text that is added.  This is an
		* implementation detail of the edit control that is unexpected
		* and can cause endless recursion when EM_REPLACESEL is sent
		* from a WM_CHAR handler.  The fix is to ignore calling the
		* handler from WM_CHAR.
		*/
		ignoreCharacter = true;
		OS.SendMessage (handle, OS.EM_REPLACESEL, 0, Converter.StrToTCHARz(newText));
		ignoreCharacter = false;
		return LDWTRESULT.ZERO;
	}
	
	/* Do the original Undo */
	ignoreVerify = true;
	callWindowProc (OS.WM_UNDO, wParam, lParam);
	ignoreVerify = false;
	return LDWTRESULT.ONE;
}

LDWTRESULT wmCommandChild (int wParam, int lParam) {
	int code = wParam >> 16;
	switch (code) {
		case OS.EN_CHANGE:
			if (ignoreModify) break;
			/*
			* It is possible (but unlikely), that application
			* code could have disposed the widget in the modify
			* event.  If this happens, end the processing of the
			* Windows message by returning zero as the result of
			* the window proc.
			*/
			sendEvent (DWT.Modify);
			if (isDisposed ()) return LDWTRESULT.ZERO;
			break;
		case OS.EN_ALIGN_LTR_EC:
			style &= ~DWT.RIGHT_TO_LEFT;
			style |= DWT.LEFT_TO_RIGHT;
			fixAlignment ();
			break;
		case OS.EN_ALIGN_RTL_EC:
			style &= ~DWT.LEFT_TO_RIGHT;
			style |= DWT.RIGHT_TO_LEFT;
			fixAlignment ();
			break;
		default : break;
	}
	return super.wmCommandChild (wParam, lParam);
}

// Event Handler
public void handleModify(Object customData, void delegate(ModifyEvent) func){
	handleEvent(new ModifyHandler(customData, DWT.Modify, func));
}
public void handleSelection(Object customData, void delegate(SelectionEvent) func){
	handleEvent(new SelectionHandler(customData, DWT.Selection, func));
}
public void handleDefaultSelection(Object customData, void delegate(SelectionEvent) func) {
	handleEvent(new SelectionHandler(customData, DWT.DefaultSelection, func));
}
public void handleVerify(Object customData, void delegate(VerifyEvent) func){
	handleEvent(new VerifyHandler(customData, DWT.Verify, func));
}

}
